package com.rockbb.common.utilities.file;

import java.awt.Dimension;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

import javax.imageio.ImageIO;

import org.apache.commons.io.FileUtils;
import org.apache.log4j.Logger;
import org.apache.sanselan.Sanselan;
import org.im4java.core.ConvertCmd;
import org.im4java.core.IMOperation;
import org.im4java.process.ProcessStarter;

import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGImageEncoder;
import com.rockbb.common.config.Config;

public class ImageUtil 
{
	public static final String EXTENSION_TEMP_FILE = ".tmp";
	public static final String[] IMAGE_TYPES = {"jpg"};
	public static final int IMAGE_TYPE_JPG = 0;

	public static final int COLOR_TYPE_UNKNOWN = -2;
	public static final int COLOR_TYPE_OTHER = -1;
	public static final int COLOR_TYPE_BW = 0;
	public static final int COLOR_TYPE_GRAYSCALE = 1;
	public static final int COLOR_TYPE_RGB = 2;
	public static final int COLOR_TYPE_CMYK = 3;

	static
	{
		ProcessStarter.setGlobalSearchPath(Config.getValue("im4j.env_path"));
	}

	private static Logger logger = Logger.getLogger(ImageUtil.class);

	/**
	 * Compress the image using im4J
	 * 
	 * @param filePath: Include the extension
	 * @param targetFilePath: Include the extension. If null or empty, it will be set to filePath
	 * @param destWidth 0: no width limit
	 * @param destHeight 0: no height limit
	 * @param type
	 * @return
	 */
	public static boolean compressImageByIM4J(String filePath, String targetFilePath, long destWidth, long destHeight, int type)
	{
		if(filePath == null || filePath.length() == 0 || (destWidth <= 0 && destHeight <= 0)) return false;
		if (targetFilePath == null || targetFilePath.length() == 0) targetFilePath = filePath;

		File file = new File(filePath);
		if (!file.isFile()) return false;

		String tmpFilePath = targetFilePath + '.' + IMAGE_TYPES[type];
		try
		{
			// Check the color type
			/*ImageInfo image_info = Sanselan.getImageInfo(file);
			if (image_info != null)
			{
				logger.info(image_info.toString());
			}*/

			// Get the dimension
			Dimension d = Sanselan.getImageSize(file);	// Sanselan is a better replacement of ImageIO, it supports the CMYK color space images
			double srcWidth = d.getWidth();
			double srcHeight = d.getHeight();
			if (srcWidth < 0.1 || srcHeight < 0.1) return false;

			if (destHeight <= 0)
			{
				if (srcWidth < destWidth) return false;
				destHeight = Math.round(srcHeight * destWidth / srcWidth);
			}
			else if (destWidth <=0)
			{
				if (srcHeight < destHeight) return false;
				destWidth = Math.round(srcWidth * destHeight / srcHeight);
			}
			else
			{
				if (srcHeight < destHeight && srcWidth < destWidth) return false;
				double srcRatio = srcHeight / srcWidth;
				double destRatio = destHeight * 1.0 / destWidth;

				if (srcRatio > destRatio)
				{
					destHeight = Math.round(srcHeight * destWidth / srcWidth);
				}
				else
				{
					destWidth = Math.round(srcWidth * destHeight / srcHeight);
				}
			}

			IMOperation op = new IMOperation();  
	        op.addImage();  
	        op.resize((int)destWidth, (int)destHeight, '^').gravity("center").extent((int)destWidth, (int)destHeight);  
	        op.addImage();  

	        ConvertCmd convert = new ConvertCmd(true);  
	        convert.run(op, filePath, tmpFilePath);  

			// After successful compression, replace the old file with the newly generated one
			if (targetFilePath.lastIndexOf('.') > 0)
				targetFilePath = targetFilePath.substring(0, targetFilePath.lastIndexOf('.')) + '.' + IMAGE_TYPES[type];
			else
				targetFilePath = targetFilePath + '.' + IMAGE_TYPES[type];

			if (!targetFilePath.equals(tmpFilePath))
			{
				File tmpFile = new File(tmpFilePath);
				if (tmpFile.isFile())
				{
					FileUtils.copyFile(tmpFile, new File(targetFilePath));
					FileUtils.deleteQuietly(tmpFile);
				}
			}
			return true;
		}
		catch(Exception e)
		{
			logger.info("[Error]File compress failed for " + filePath);
			logger.debug(e.getMessage(), e);
		}
		return false;
	}

	/**
	 * Compress the image using native Java image functions. 
	 * 
	 * @param srcFile 传入文件路径
	 * @param descWidth 目标文件宽度
	 * @param descHeighth 目标文件高度
	 * @param postfix 目标文件后缀名
	 * @return 
	 */
	public static boolean compressImageByJavaNative(String filePath, long destWidth, long destHeight, int type)
	{
		if(filePath == null || filePath.length() == 0 || (destWidth <= 0 && destHeight <= 0)) return false;

		File file = new File(filePath);
		if (!file.exists()) return false;

		try
		{
			BufferedImage image = ImageIO.read(file);
			double srcWidth = image.getWidth();
			double srcHeight = image.getHeight();
			if (srcWidth < 0.1 || srcHeight < 0.1) return false;

			if (destHeight <= 0)
			{
				if (srcWidth < destWidth) return false;
				destHeight = Math.round(srcHeight * destWidth / srcWidth);
			}
			else if (destWidth <=0)
			{
				if (srcHeight < destHeight) return false;
				destWidth = Math.round(srcWidth * destHeight / srcHeight);
			}
			else
			{
				if (srcHeight < destHeight && srcWidth < destWidth) return false;
				double srcRatio = srcHeight / srcWidth;
				double destRatio = destHeight * 1.0 / destWidth;

				if (srcRatio > destRatio)
				{
					destHeight = Math.round(srcHeight * destWidth / srcWidth);
				}
				else
				{
					destWidth = Math.round(srcWidth * destHeight / srcHeight);
				}
			}

			resizeImage(image, filePath + EXTENSION_TEMP_FILE, (int)destWidth, (int)destHeight);

			// After successful compression, replace the old file with the newly generated one
			File tmpFile = new File(filePath + EXTENSION_TEMP_FILE);
			if (tmpFile.isFile())
			{
				String newFilePath = filePath.substring(0, filePath.lastIndexOf('.')) + '.' + IMAGE_TYPES[type];
				FileUtils.copyFile(tmpFile, new File(newFilePath));
				FileUtils.deleteQuietly(tmpFile);
			}
			return true;
		}
		catch(Exception e)
		{
			logger.info("[Error]File compress failed for " + filePath);
			logger.debug(e.getMessage(), e);
		}
		return false;
	}

	/**
	 * 通过调用图片编辑器命令 (convert) 实现图片缩放 按照给定宽 高 等比例缩放
	 * 
	 * @param srcPath
	 *            原图全路径（包括文件名）
	 * @param descPath
	 *            目标图全路径（包括文件名）
	 * @param width
	 *            图片高度
	 * @param height
	 *            图片宽度
	 */
	public static final void compressImageByCMD(String srcPath, String descPath, int width, int height) throws Exception {
		
		if(srcPath == null || descPath == null || width == 0 || height == 0){
			logger.error("convertImage 传入图片高度或宽度错误");
			throw new Exception("convertImage 传入图片高度或宽度错误");
		}
		
		Runtime Deruntime = Runtime.getRuntime();
		StringBuffer sb = new StringBuffer(300);
		try {
			sb.append(Config.getValue("imageMagick.convert.path")).append(" -resize ")
				.append(width).append("x").append(height)
				.append(" -quality 90 -colorspace rgb ").append(srcPath)
				.append(" ").append(descPath.toString());

			Deruntime.exec(sb.toString());
			Deruntime.freeMemory();
			Thread.sleep(4000);
		} catch (Exception e) {
			logger.error(e.toString());
		} finally {
			sb = null;
		}
	}

	/**
	 * Compress image with JPEG Image Encoder
	 * 
	 * @param sourceImage
	 * @param descPath
	 * @param destWidth
	 * @param destHeight
	 * @throws Exception
	 */
	public static void resizeImage(BufferedImage sourceImage, String descPath, int destWidth, int destHeight) 
			throws IOException, FileNotFoundException
	{
		BufferedImage dest = new BufferedImage(destWidth, destHeight, BufferedImage.TYPE_INT_RGB);
		dest.getGraphics().drawImage(sourceImage, 0, 0, destWidth, destHeight, null);
		File tmpFile = new File(descPath);
		FileOutputStream os = new FileOutputStream(tmpFile);
		JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(os);
		encoder.encode(dest);
		os.close();
	}

	/**
	 * Returns a description of the color type of the image.
	 * 
	 * @see #getColorType()
	 */
	public static String getColorTypeDescription(int colorType)
	{
		switch (colorType)
		{
			case COLOR_TYPE_BW:
				return "BW";
			case COLOR_TYPE_GRAYSCALE:
				return "Grayscale";
			case COLOR_TYPE_RGB:
				return "RGB";
			case COLOR_TYPE_CMYK:
				return "CMYK";
			case COLOR_TYPE_OTHER:
				return "Other";
			case COLOR_TYPE_UNKNOWN:
				return "Unknown";
	
			default:
				return "Unknown";
		}
	}
}
